Skip to content

OpenClaw 网关配置与插件开发

网关(Gateway)是 OpenClaw 的核心服务,负责调度、工具执行、会话管理等功能。本文详解网关配置、插件开发、服务管理等高级主题。

概述

OpenClaw 网关提供:

  • 🔄 会话调度与管理
  • 🛠️ 工具执行与编排
  • 🔌 插件系统扩展
  • 📡 远程节点连接
  • ⏰ 定时任务调度
  • 🔐 安全与权限控制

一、网关架构

1.1 核心组件

┌─────────────────────────────────────────────────────┐
│                   OpenClaw Gateway                  │
├─────────────────────────────────────────────────────┤
│  Session Manager  │  会话创建、调度、状态管理       │
│  Tool Executor    │  工具调用、权限检查、结果返回   │
│  Plugin Loader    │  插件加载、注册、生命周期       │
│  Cron Scheduler   │  定时任务、唤醒事件             │
│  Node Manager     │  远程节点连接、设备管理         │
│  Config Manager   │  配置加载、热更新、验证         │
│  Security Module  │  认证、授权、审计               │
└─────────────────────────────────────────────────────┘

1.2 配置文件结构

yaml
# ~/.openclaw/config.yaml
gateway:
  bind: "0.0.0.0:8080"        # 监听地址
  remote:
    enabled: true              # 启用远程连接
    url: "https://your-domain.com"
  
plugins:
  entries:
    - name: "feishu"
      enabled: true
      config:
        appId: "cli_xxx"
    - name: "device-pair"
      enabled: true
      config:
        publicUrl: "https://your-domain.com"

models:
  default: "modelstudio/qwen3.5-plus"
  overrides:
    coding: "claude-sonnet-4-5-20250929"
    analysis: "modelstudio/qwen3.5-plus"

sessions:
  maxConcurrent: 10
  defaultTimeout: 300

security:
  execPolicy: "allowlist"      # deny | allowlist | full
  toolRestrictions:
    - tool: "exec"
      requireApproval: true

二、配置管理

2.1 查看配置

bash
# 查看当前配置
openclaw gateway config.get

# 查看配置 schema
openclaw gateway config.schema.lookup --path "plugins.entries"

# 验证配置
openclaw gateway config.validate

2.2 修改配置

方法 1:使用 config.patch(推荐)

bash
# 部分更新配置
openclaw gateway config.patch \
  --raw '{"plugins": {"entries": [{"name": "feishu", "enabled": true}]}}' \
  --note "启用 Feishu 插件"

方法 2:直接编辑配置文件

bash
# 编辑配置
vim ~/.openclaw/config.yaml

# 重启网关使配置生效
openclaw gateway restart

方法 3:使用 gateway 工具

javascript
// 在会话中修改配置
await gateway({
  action: 'config.patch',
  raw: JSON.stringify({
    models: { default: 'claude-sonnet-4-5-20250929' }
  }),
  note: '切换默认模型'
})

2.3 配置热更新

javascript
async function updateConfigSafely(changes, note) {
  // 1. 备份当前配置
  const currentConfig = await read({
    path: '~/.openclaw/config.yaml'
  })
  await write({
    path: '~/.openclaw/config.yaml.bak',
    content: currentConfig
  })
  
  // 2. 应用配置变更
  try {
    await gateway({
      action: 'config.patch',
      raw: JSON.stringify(changes),
      note
    })
    
    console.log('✅ 配置更新成功')
    
  } catch (error) {
    console.error('❌ 配置更新失败:', error)
    
    // 3. 回滚
    await exec({
      command: 'cp ~/.openclaw/config.yaml.bak ~/.openclaw/config.yaml'
    })
    
    throw error
  }
}

// 使用
await updateConfigSafely(
  { models: { default: 'new-model' } },
  '测试新模型'
)

2.4 实战案例 1:模型切换器

javascript
class ModelSwitcher {
  constructor() {
    this.models = {
      default: 'modelstudio/qwen3.5-plus',
      coding: 'claude-sonnet-4-5-20250929',
      analysis: 'modelstudio/qwen3.5-plus',
      creative: 'claude-opus-4-5-20251101',
      fast: 'modelstudio/qwen3.5-plus'
    }
    this.current = 'default'
  }
  
  async switch(modelType) {
    if (!this.models[modelType]) {
      throw new Error(`未知模型类型:${modelType}`)
    }
    
    const previous = this.current
    this.current = modelType
    
    await gateway({
      action: 'config.patch',
      raw: JSON.stringify({
        models: { default: this.models[modelType] }
      }),
      note: `切换模型:${previous} → ${modelType}`
    })
    
    console.log(`模型已切换:${modelType} (${this.models[modelType]})`)
    return previous
  }
  
  async forTask(modelType, task) {
    const previous = await this.switch(modelType)
    
    try {
      return await task()
    } finally {
      await this.switch(previous)
    }
  }
  
  getStatus() {
    return {
      current: this.current,
      model: this.models[this.current],
      available: Object.keys(this.models)
    }
  }
}

// 使用
const switcher = new ModelSwitcher()

// 临时切换模型执行任务
const result = await switcher.forTask('coding', async () => {
  return await sessions_spawn({
    task: '编写一个 Python 函数...',
    mode: 'run'
  })
})

三、插件系统

3.1 插件类型

OpenClaw 支持多种插件:

类型用途示例
工具插件提供新工具feishu, weather
消息插件消息通道discord, telegram
设备插件节点连接device-pair
存储插件数据存储s3, gcs
认证插件身份验证oauth, jwt

3.2 插件结构

~/.openclaw/plugins/
└── my-plugin/
    ├── plugin.json          # 插件元数据
    ├── index.js             # 入口文件
    ├── tools/               # 工具定义
    │   └── my-tool.js
    ├── config.schema.json   # 配置 schema
    └── README.md            # 文档

plugin.json 示例:

json
{
  "name": "my-plugin",
  "version": "1.0.0",
  "description": "我的自定义插件",
  "author": "你的名字",
  "main": "index.js",
  "tools": ["my-tool", "another-tool"],
  "config": {
    "apiKey": {
      "type": "string",
      "required": true,
      "description": "API 密钥"
    },
    "timeout": {
      "type": "number",
      "default": 30,
      "description": "超时时间(秒)"
    }
  },
  "dependencies": {
    "node-fetch": "^3.0.0"
  }
}

3.3 开发工具插件

示例:创建天气查询工具

javascript
// ~/.openclaw/plugins/weather-plugin/tools/weather.js
const fetch = require('node-fetch')

module.exports = {
  name: 'weather',
  description: '查询天气信息',
  
  // 参数 schema
  parameters: {
    type: 'object',
    properties: {
      location: {
        type: 'string',
        description: '地点名称'
      },
      days: {
        type: 'number',
        default: 1,
        description: '预报天数'
      }
    },
    required: ['location']
  },
  
  // 执行函数
  async execute(params, context) {
    const { location, days = 1 } = params
    
    try {
      // 调用 wttr.in API
      const response = await fetch(
        `https://wttr.in/${encodeURIComponent(location)}?format=j${days}`
      )
      
      if (!response.ok) {
        throw new Error(`天气查询失败:${response.statusText}`)
      }
      
      const data = await response.json()
      
      // 格式化结果
      return {
        location: data.nearest_area[0].areaName[0].value,
        current: {
          temp: data.current_condition[0].temp_C,
          condition: data.current_condition[0].weatherDesc[0].value,
          humidity: data.current_condition[0].humidity,
          wind: data.current_condition[0].windspeedKmph
        },
        forecast: data.weather?.slice(0, days).map(day => ({
          date: day.date,
          maxTemp: day.maxtempC,
          minTemp: day.mintempC,
          condition: day.hourly[12].weatherDesc[0].value
        }))
      }
      
    } catch (error) {
      throw new Error(`天气查询错误:${error.message}`)
    }
  }
}

注册工具:

javascript
// ~/.openclaw/plugins/weather-plugin/index.js
const weatherTool = require('./tools/weather')

module.exports = {
  name: 'weather-plugin',
  
  // 插件初始化
  async init(config) {
    console.log('天气插件初始化')
    this.config = config
  },
  
  // 注册工具
  getTools() {
    return [weatherTool]
  },
  
  // 插件清理
  async cleanup() {
    console.log('天气插件清理')
  }
}

3.4 配置 schema

json
// ~/.openclaw/plugins/weather-plugin/config.schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "apiKey": {
      "type": "string",
      "description": "可选的 API 密钥(用于高级功能)"
    },
    "defaultLocation": {
      "type": "string",
      "description": "默认查询地点"
    },
    "cacheTimeout": {
      "type": "number",
      "default": 600,
      "description": "缓存超时(秒)"
    }
  }
}

3.5 实战案例 2:自定义通知插件

javascript
// ~/.openclaw/plugins/notify-plugin/index.js
const fetch = require('node-fetch')

class NotifyPlugin {
  constructor() {
    this.channels = new Map()
  }
  
  async init(config) {
    this.config = config
    
    // 注册通知渠道
    if (config.channels) {
      for (const channel of config.channels) {
        this.channels.set(channel.name, channel)
      }
    }
  }
  
  getTools() {
    return [
      {
        name: 'notify',
        description: '发送通知到指定渠道',
        parameters: {
          type: 'object',
          properties: {
            channel: { type: 'string' },
            title: { type: 'string' },
            message: { type: 'string' },
            priority: { 
              type: 'string',
              enum: ['low', 'normal', 'high', 'urgent']
            }
          },
          required: ['channel', 'message']
        },
        execute: async (params) => this.sendNotification(params)
      },
      {
        name: 'notify_broadcast',
        description: '广播通知到所有渠道',
        parameters: {
          type: 'object',
          properties: {
            title: { type: 'string' },
            message: { type: 'string' }
          },
          required: ['message']
        },
        execute: async (params) => this.broadcast(params)
      }
    ]
  }
  
  async sendNotification({ channel, title, message, priority = 'normal' }) {
    const config = this.channels.get(channel)
    
    if (!config) {
      throw new Error(`未知渠道:${channel}`)
    }
    
    switch (config.type) {
      case 'webhook':
        return await this.sendWebhook(config.url, { title, message, priority })
      
      case 'email':
        return await this.sendEmail(config, { title, message })
      
      case 'sms':
        return await this.sendSms(config, message)
      
      default:
        throw new Error(`不支持的渠道类型:${config.type}`)
    }
  }
  
  async sendWebhook(url, payload) {
    const response = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    })
    
    if (!response.ok) {
      throw new Error(`Webhook 发送失败:${response.statusText}`)
    }
    
    return { success: true, channel: 'webhook' }
  }
  
  async sendEmail(config, { title, message }) {
    // 使用 nodemailer 或其他邮件库
    // 这里简化处理
    console.log(`发送邮件到 ${config.to}: ${title}`)
    return { success: true, channel: 'email' }
  }
  
  async sendSms(config, message) {
    // 使用 SMS API
    console.log(`发送短信到 ${config.phone}: ${message}`)
    return { success: true, channel: 'sms' }
  }
  
  async broadcast({ title, message }) {
    const results = []
    
    for (const [name, config] of this.channels) {
      try {
        const result = await this.sendNotification({
          channel: name,
          title,
          message,
          priority: 'normal'
        })
        results.push({ channel: name, ...result })
      } catch (error) {
        results.push({ channel: name, success: false, error: error.message })
      }
    }
    
    return results
  }
}

module.exports = new NotifyPlugin()

四、服务管理

4.1 启动/停止/重启

bash
# 查看网关状态
openclaw gateway status

# 启动网关
openclaw gateway start

# 停止网关
openclaw gateway stop

# 重启网关
openclaw gateway restart

# 重启并延迟
openclaw gateway restart --delay 5000

4.2 日志查看

bash
# 查看实时日志
openclaw gateway logs --follow

# 查看最近 100 行
openclaw gateway logs --lines 100

# 查看错误日志
openclaw gateway logs --level error

# 导出日志
openclaw gateway logs --output gateway.log

4.3 健康检查

javascript
async function gatewayHealthCheck() {
  const checks = [
    {
      name: '网关状态',
      fn: async () => {
        const status = await gateway({ action: 'status' })
        return status.running ? 'ok' : 'fail'
      }
    },
    {
      name: '配置加载',
      fn: async () => {
        const config = await gateway({ action: 'config.get' })
        return config ? 'ok' : 'fail'
      }
    },
    {
      name: '插件状态',
      fn: async () => {
        // 检查关键插件是否加载
        return 'ok'
      }
    },
    {
      name: '模型连接',
      fn: async () => {
        // 测试模型连接
        const result = await sessions_spawn({
          task: 'ping',
          mode: 'run',
          timeoutSeconds: 10
        })
        return result ? 'ok' : 'fail'
      }
    }
  ]
  
  const results = {}
  
  for (const check of checks) {
    try {
      results[check.name] = await check.fn()
    } catch (error) {
      results[check.name] = `fail: ${error.message}`
    }
  }
  
  return results
}

// 定期健康检查
setInterval(async () => {
  const health = await gatewayHealthCheck()
  console.log('网关健康检查:', health)
}, 5 * 60 * 1000)  // 每 5 分钟

4.4 实战案例 3:自动恢复服务

javascript
class GatewayAutoRecovery {
  constructor() {
    this.consecutiveFailures = 0
    this.maxFailures = 3
    this.checkInterval = 60000  // 1 分钟
  }
  
  async start() {
    console.log('启动网关自动恢复监控')
    
    setInterval(() => this.checkAndRecover(), this.checkInterval)
  }
  
  async checkAndRecover() {
    try {
      // 健康检查
      const status = await gateway({ action: 'status' })
      
      if (!status.running) {
        throw new Error('网关未运行')
      }
      
      // 重置失败计数
      this.consecutiveFailures = 0
      
    } catch (error) {
      this.consecutiveFailures++
      console.error(`健康检查失败 (${this.consecutiveFailures}/${this.maxFailures}):`, error.message)
      
      if (this.consecutiveFailures >= this.maxFailures) {
        await this.recover()
      }
    }
  }
  
  async recover() {
    console.log('⚠️ 连续失败,尝试恢复...')
    
    try {
      // 1. 尝试重启
      console.log('重启网关...')
      await gateway({
        action: 'restart',
        note: '自动恢复:连续健康检查失败'
      })
      
      // 2. 等待启动
      await sleep(10000)
      
      // 3. 验证恢复
      const status = await gateway({ action: 'status' })
      
      if (status.running) {
        console.log('✅ 网关恢复成功')
        this.consecutiveFailures = 0
        
        // 通知用户
        await this.notifyRecovery('success')
      } else {
        throw new Error('重启后仍未运行')
      }
      
    } catch (error) {
      console.error('❌ 恢复失败:', error)
      
      // 通知用户
      await this.notifyRecovery('failed', error)
    }
  }
  
  async notifyRecovery(status, error = null) {
    const message = status === 'success'
      ? '✅ 网关已自动恢复'
      : `❌ 网关恢复失败:${error?.message}`
    
    try {
      await message({
        action: 'send',
        target: '老大',
        message: `🔧 网关自动恢复通知\n\n${message}\n时间:${new Date().toLocaleString('zh-CN')}`
      })
    } catch (e) {
      console.error('发送通知失败:', e)
    }
  }
}

// 使用
const recovery = new GatewayAutoRecovery()
recovery.start()

五、远程节点

5.1 节点连接配置

yaml
# ~/.openclaw/config.yaml
plugins:
  entries:
    - name: "device-pair"
      enabled: true
      config:
        publicUrl: "https://your-domain.com"  # 公网可访问地址
        port: 8080

5.2 节点管理

javascript
// 列出节点
async function listNodes() {
  return await nodes({ action: 'status' })
}

// 获取节点详情
async function getNodeInfo(deviceId) {
  return await nodes({
    action: 'device_info',
    deviceId
  })
}

// 发送通知到节点
async function notifyNode(deviceId, title, body) {
  return await nodes({
    action: 'notify',
    deviceId,
    title,
    body,
    priority: 'active'
  })
}

// 获取节点位置
async function getNodeLocation(deviceId) {
  return await nodes({
    action: 'location_get',
    deviceId,
    desiredAccuracy: 'balanced'
  })
}

// 截取屏幕
async function captureNodeScreen(deviceId) {
  return await nodes({
    action: 'screen_record',
    deviceId,
    durationMs: 5000
  })
}

5.3 实战案例 4:节点监控仪表板

javascript
class NodeMonitor {
  constructor() {
    this.nodes = new Map()
  }
  
  async refresh() {
    const status = await nodes({ action: 'status' })
    
    for (const node of status.nodes || []) {
      const info = await nodes({
        action: 'device_info',
        deviceId: node.id
      })
      
      this.nodes.set(node.id, {
        ...node,
        info,
        lastSeen: Date.now()
      })
    }
    
    return this.getDashboard()
  }
  
  getDashboard() {
    const nodes = Array.from(this.nodes.values())
    
    return {
      total: nodes.length,
      online: nodes.filter(n => n.connected).length,
      offline: nodes.filter(n => !n.connected).length,
      battery: nodes.map(n => ({
        name: n.name,
        level: n.info?.battery?.level,
        charging: n.info?.battery?.charging
      })),
      locations: nodes.map(n => ({
        name: n.name,
        location: n.info?.location
      }))
    }
  }
  
  async generateReport() {
    const dashboard = await this.refresh()
    
    let report = `# 节点监控报告\n\n`
    report += `## 概览\n`
    report += `- 总节点数:${dashboard.total}\n`
    report += `- 在线:${dashboard.online}\n`
    report += `- 离线:${dashboard.offline}\n\n`
    
    report += `## 电池状态\n`
    for (const node of dashboard.battery) {
      const icon = node.level > 50 ? '🔋' : node.level > 20 ? '🪫' : '⚠️'
      report += `- ${node.name}: ${icon} ${node.level}%${node.charging ? ' (充电中)' : ''}\n`
    }
    
    report += `\n## 位置信息\n`
    for (const node of dashboard.locations) {
      if (node.location) {
        report += `- ${node.name}: ${node.location.address}\n`
      }
    }
    
    return report
  }
  
  async checkAlerts() {
    const alerts = []
    
    for (const [id, node] of this.nodes) {
      // 低电量告警
      if (node.info?.battery?.level < 20) {
        alerts.push({
          type: 'low_battery',
          node: node.name,
          level: node.info.battery.level
        })
      }
      
      // 离线告警
      if (!node.connected && Date.now() - node.lastSeen > 300000) {
        alerts.push({
          type: 'offline',
          node: node.name,
          since: node.lastSeen
        })
      }
    }
    
    return alerts
  }
}

// 使用
const monitor = new NodeMonitor()

// 定期生成报告
async function nodeMonitoringJob() {
  const report = await monitor.generateReport()
  console.log(report)
  
  // 检查告警
  const alerts = await monitor.checkAlerts()
  for (const alert of alerts) {
    console.log('⚠️ 告警:', alert)
  }
}

六、安全配置

6.1 执行策略

yaml
# ~/.openclaw/config.yaml
security:
  # 执行策略
  execPolicy: "allowlist"  # deny | allowlist | full
  
  # 允许的命令列表
  execAllowlist:
    - "ls"
    - "cat"
    - "grep"
    - "find"
    - "git"
    - "npm"
    - "node"
  
  # 需要审批的命令
  execRequireApproval:
    - "rm"
    - "sudo"
    - "curl"
    - "wget"
  
  # 禁止的命令
  execDenylist:
    - "mkfs"
    - "dd"
    - "shutdown"

6.2 工具限制

yaml
security:
  toolRestrictions:
    - tool: "exec"
      requireApproval: true
      maxTimeout: 300
    
    - tool: "browser"
      allowedDomains:
        - "*.github.com"
        - "*.feishu.cn"
    
    - tool: "message"
      allowedChannels:
        - "feishu"
        - "discord"

6.3 审计日志

javascript
async function enableAuditLogging() {
  // 配置审计
  await gateway({
    action: 'config.patch',
    raw: JSON.stringify({
      security: {
        auditLog: {
          enabled: true,
          path: '~/.openclaw/logs/audit.log',
          events: ['exec', 'message', 'config.change']
        }
      }
    }),
    note: '启用审计日志'
  })
}

// 查看审计日志
async function viewAuditLog(options = {}) {
  const { lines = 100, event = null } = options
  
  let command = `tail -${lines} ~/.openclaw/logs/audit.log`
  
  if (event) {
    command += ` | grep "${event}"`
  }
  
  return await exec({ command })
}

七、性能优化

7.1 并发控制

yaml
sessions:
  maxConcurrent: 10          # 最大并发会话数
  maxConcurrentPerUser: 5    # 每用户最大并发
  
subagents:
  maxConcurrent: 5           # 最大并发了代理
  queueEnabled: true         # 启用队列

7.2 超时配置

yaml
timeouts:
  default: 300               # 默认超时(秒)
  exec: 60                   # 命令执行超时
  browser: 120               # 浏览器操作超时
  api: 30                    # API 调用超时
  total: 1800                # 总任务超时(30 分钟)

7.3 缓存配置

yaml
cache:
  enabled: true
  memory:
    maxSize: 512             # MB
  disk:
    path: "~/.openclaw/cache"
    maxSize: 2048            # MB
  ttl:
    default: 3600            # 秒
    api: 600                 # API 响应缓存
    search: 1800             # 搜索结果缓存

八、总结

核心要点

  1. 网关配置决定系统行为
  2. 插件系统提供无限扩展
  3. 安全配置保护系统安全
  4. 监控和恢复保障稳定性
  5. 性能优化提升用户体验

进阶方向

  • 📖 阅读网关源码
  • 🔧 开发自定义插件
  • 🏗️ 构建企业级部署
  • 📊 实现监控告警系统

🟢🐉 开始定制你的 OpenClaw 网关吧!

Released under the MIT License.